/*************************************************************************
** Name		: DbgUtil.h
** Desc		: 打印日志。阻塞方式，多线程安全，输出带文件名、线程id、时间、行号等，支持文件、控制台、回调输出
** Todo     : 1.文件过大时应新建文件输出；2.启动时清理上次过时日志；3.完善级别输出；
** Author	: xktesla
*************************************************************************/

#ifndef _BDC70D66_77A7_498F_9EF9_627BCE0B3E34
#define _BDC70D66_77A7_498F_9EF9_627BCE0B3E34


#include "depends/incfile.h"
#include "xCores.h"
#include "ISingleton.h"

namespace x2lib
{
#define BUFF_SIZE		(15*1024) // 一条日志所占最大空间

    /**
    * @brief        日志打印，模板参数含义依次
    * @param[in]    pszDir 输出路径，可忽略尾部“/”或“\”，nullptr表示当前目录
    * @param[in]    nExpDays 过期天数，在下次初始化时会将pszDir路径下过期的文件删除
    * @param[in]    eLevel 日志级别，低级别包括高级别，如传入Warn表示Warn/Error/Fatal都将输出，Info/Debug不输出
    * @param[in]    arrFilter 筛选器，nullptr表示忽略，否则只输出包含arrFilter指定的关键词的日志，注意不能超过5个，且每个关键词的字符应小于64个
    * @param[in]    uOutBits 输出方式，通过位组合设置，0x01控制台，0x02文件，0x03回调函数
    * @param[in]    callback 接收日志的回调函数
    * @return       是否成功
    * @author       xktesla
    */
    class DbgUtil: public ISingleton<DbgUtil, const char*, uint8_t, uint8_t, const char[5][64], uint8_t, void(*)(const char*)>
    {
        friend class ISingleton<DbgUtil, const char*, uint8_t, uint8_t, const char[5][64], uint8_t, void(*)(const char*)>;

    public:
        enum EOutBit
        {
            OutBit_None = 0,
            OutBit_Console = 0x01,
            OutBit_File = 0x02,
            OutBit_Callback = 0x04,
            OutBit_All = 0xFF,
        };
        // 勿改变枚举顺序
        enum ELevel
        {
            Level_Info = 0, Level_Debug, Level_Warn, Level_Error, Level_Fatal, Level_Text
        };


        /**
        * @brief        格式化输出日志
        * @param[in]    eLevel 日志级别
        * @param[in]    pszFile 当前源码文件
        * @param[in]    nLine 行号
        * @param[in]    pszFmt 格式化
        * @param[in]    ... 
        * @return
        * @author       xktesla
        */
        void Out(ELevel eLevel, const char *pszFile, int nLine, const char *pszFmt, ...);

        /**
        * @brief        弹出MessageBox框，仅Windows可用，最多显示1024个字符
        * @param[in]    eLevel 日志级别
        * @param[in]    nLine 行号
        * @param[in]    pszFmt 格式化
        * @param[in]    ... 
        * @return       
        * @author       xktesla
        */
        void Box(ELevel eLevel, int nLine, const char * pszFmt, ...);

        /**
        * @brief        输出一个二进制文件
        * @param[in]    eLevel 日志级别
        * @param[in]    pszFile 当前源码文件
        * @param[in]    nLine 行号
        * @param[in]    pszBinName 输出文件名，不含路径
        * @param[in]    pData 数据指针
        * @param[in]    nData 数据大小
        * @return
        * @author       xktesla
        */
        void Bin(ELevel eLevel, const char * pszFile, int nLine, const char *pszBinName, const char *pData, uint32_t nData);

    protected:
        bool testLevel(ELevel eLevel);
        bool testFilter(const char* pszLog);

    private:
        DbgUtil(const char* pszDir, uint8_t nExpDays, uint8_t eLevel, const char arrFilter[5][64], uint8_t uOutBits, void(*callback)(const char*));
        virtual ~DbgUtil();

        virtual bool initInst() override;

        /**
        * @brief        反初始化接口，为派生类提供一次反初始化机会
        * @author       xktesla
        */
        virtual void unInitInst() override;

        char m_szFile[512];
        char m_szOutDir[512];
        char *m_pLogBuf;
        ELevel m_eLevel;
        void(*m_pCallback)(const char*);
        char m_arrFilter[5][64];
        bool m_ignoreFilter;
        EOutBit m_uOutBits;
        const static char m_strLvl[6][16];
        xCores::Mutex m_mutex;
    };

#ifdef __X2LIB_RELEASE__
    #define LOG_INIT(args...)
    #define LOG_PRINT_I(fmt,args...)
    #define LOG_PRINT_E(fmt,args...)
    #define LOG_PRINT_W(fmt,args...)
    
    #ifdef __X2LIB_WIN32__
        #define LOG_PRINT_T(fmt,args...)                    printf(fmt,__VA_ARGS__)
    #else
        #define LOG_PRINT_T(fmt,args...)                    printf(fmt,##args)
    #endif
    #define LOG_UNINIT()

    #define ENTER()
    #define EXIT()
    #define RETURN(_x_)                                 return _x_
#else
    #define LOG_INIT                                    DbgUtil::GetInst()->InitInst
    #define LOG_UNINIT                                  DbgUtil::GetInst()->UnInitInst
    #define LOG_OUTBIN(pszFileName, pData, nData)       DbgUtil::GetInst()->Bin(DbgUtil::Level_Debug,__FILE__,__LINE__, pszFileName, pData, nData)

    #ifdef __X2LIB_WIN32__
        #define LOG_PRINT_F(fmt,...)                    DbgUtil::GetInst()->Out(DbgUtil::Level_Fatal,__FILE__,__LINE__,fmt,__VA_ARGS__)
        #define LOG_PRINT_T(fmt,...)                    DbgUtil::GetInst()->Out(DbgUtil::Level_Text,__FILE__,__LINE__,fmt,__VA_ARGS__)
    #else
        #define LOG_PRINT_F(fmt,args...)                DbgUtil::GetInst()->Out(DbgUtil::Level_Fatal,__FILE__,__LINE__,fmt,##args)
        #define LOG_PRINT_T(fmt,args...)                DbgUtil::GetInst()->Out(DbgUtil::Level_Text,__FILE__,__LINE__,fmt,##args)
    #endif

    #ifdef __X2LIB_DEBUG__
        #ifdef __X2LIB_WIN32__
            #define LOG_PRINT_I(fmt,...)               DbgUtil::GetInst()->Out(DbgUtil::Level_Info,__FILE__,__LINE__,fmt,__VA_ARGS__)
            #define LOG_PRINT_D(fmt,...)               DbgUtil::GetInst()->Out(DbgUtil::Level_Debug,__FILE__,__LINE__,fmt,__VA_ARGS__)
            #define LOG_PRINT_E(fmt,...)               DbgUtil::GetInst()->Out(DbgUtil::Level_Error,__FILE__,__LINE__,fmt,__VA_ARGS__)
            #define LOG_PRINT_W(fmt,...)               DbgUtil::GetInst()->Out(DbgUtil::Level_Warn,__FILE__,__LINE__,fmt,__VA_ARGS__)
            #ifndef ASSERT
                #define ASSERT(x)                      {if(!(x)) _asm{int 0x03}}
            #endif                                     
            #ifndef VERIFY                             
                #define VERIFY(x)                      {if(!(x)) _asm{int 0x03}} 
            #endif
            #define MSGBOX(fmt,...)						
        #else
            #define LOG_PRINT_I(fmt,args...)           DbgUtil::GetInst()->Out(DbgUtil::Level_Info,__FILE__,__LINE__,fmt,##args)
            #define LOG_PRINT_D(fmt,args...)           DbgUtil::GetInst()->Out(DbgUtil::Level_Debug,__FILE__,__LINE__,fmt,##args)
            #define LOG_PRINT_E(fmt,args...)           DbgUtil::GetInst()->Out(DbgUtil::Level_Error,__FILE__,__LINE__,fmt,##args)
            #define LOG_PRINT_W(fmt,args...)           DbgUtil::GetInst()->Out(DbgUtil::Level_Warn,__FILE__,__LINE__,fmt,##args)
            #define ASSERT(x)
            #define VERIFY(x)
        #endif // __X2LIB_WIN32__

        #define ENTER()                                     do{LOG_PRINT_D("-> %s \n", __FUNCTION__);} while (0)
        #define EXIT()                                      do{LOG_PRINT_D("<- %s \n", __FUNCTION__);return;} while (0)
        #define RETURN(_x_)                                 do{LOG_PRINT_D("<- %s return %d\n", __FUNCTION__, _x_);return _x_;} while (0)
    #else
        #ifdef __X2LIB_WIN32__
            #define LOG_PRINT_I(fmt,...)
            #define LOG_PRINT_D(fmt,...)
            #define LOG_PRINT_E(fmt,...)
            #define LOG_PRINT_W(fmt,...)
        #else
            #define LOG_PRINT_I(fmt,args...)
            #define LOG_PRINT_D(fmt,args...)
            #define LOG_PRINT_E(fmt,args...)
            #define LOG_PRINT_W(fmt,args...)
        #endif
        #define ASSERT(x)
        #define VERIFY(x)

        #define ENTER() 
        #define EXIT() 
        #define RETURN(_x_)                                 return _x_
    #endif

#endif

}

#endif

